﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Globalization;

using log4net;
using log4net.Config;

using LuaInterface;
using Jint;

namespace MND
{
    class MainMND
    {
        private static bool exit = false;
        private static Dictionary<String, MethodInfo> commands = new Dictionary<string, MethodInfo>();

        static void Main(string[] args)
        {
            // === OBJECTS ===

            // TelnetServer
            Telnet.Server tServer;

            // Game
            Game game = new Game();
           
            

            // log4Net
            Console.WriteLine("Configuring log4net");
            XmlConfigurator.Configure();


            // === LOADING ===

            // Worlds
            Console.WriteLine("Loading worlds");
            Worlds.DBWorldsDataContext WorldsDB = new Worlds.DBWorldsDataContext();
            var WorldsToLoad = (from world in WorldsDB.dbWorlds
                                where world.Enabled
                                select world);

            foreach (var world in WorldsToLoad)
            {
                Console.WriteLine("Loading world: " + world.Name);
                Worlds.World w = Worlds.World.Load(world.Name);
                game.LoadedWorlds.Add(world.Name, w);
            }

            Console.WriteLine("Worlds loaded.");

            // Telnet server
            Console.WriteLine("Loading Telnet.Server");
            tServer = new Telnet.Server();

            // User Manager
            Console.WriteLine("Loading UserManager");
            game.UserManager = new Users.UserManager(tServer);



            Console.WriteLine("Loading complete.");

            // === STARTING ===
            // Telnet Server
            tServer.Run();

            // Game
            game.Start();


            Console.WriteLine("Preparing command line.");
            PrepareMainCommands();

            while (!exit)
            {
                Console.Write("MND>");
                HandleInput(Console.ReadLine());

            }
            //Console.WriteLine("Unloading {}");

            Console.WriteLine("Unloading Game");
            game.Stop();

            Console.WriteLine("Unloading Telnet.Server");
            tServer.Stop();
          

            Console.WriteLine("Unloading complete.");
            Console.WriteLine("MND is quiting.");
        }

        private static void PrepareMainCommands()
        {
            MethodInfo[] mInfos = typeof(MainMND).GetMethods(BindingFlags.Static| BindingFlags.NonPublic);
            foreach (MethodInfo method in mInfos)
            {
                object[] attributes = method.GetCustomAttributes(typeof(MainCommand), false);
                if (attributes.Length > 0)
                {
                    commands.Add(method.Name.ToUpperInvariant(), method);
                }
            }
        }

        private static void HandleInput(string input)
        {
            input = input.Trim();
            if (input == String.Empty)
                return;

            string command = String.Empty, parameters = String.Empty;
            if (input.Contains(' '))
            {
                command = input.Substring(0, input.IndexOf(' '));
                parameters = input.Substring(input.IndexOf(' ') + 1);
            }
            else
            {
                command = input;
            }
            command = command.ToUpperInvariant();
            if (commands.ContainsKey(command))
            {
                MethodInfo method = commands[command];
                method.Invoke(null, new object[] { parameters });
            }
            else
            {
                Console.WriteLine("No such command");
            }

        }

        // Commands
        #region Commands
        [MainCommand]
        private static void TestCommand(string p){
            Console.WriteLine("In TestCommand");
        }

        [MainCommand]
        private static void Shutdown(string p)
        {
            if (p != String.Empty)
            {
                int waitSec;
                if (!Int32.TryParse(p, out waitSec))
                {
                    Console.WriteLine("Wrong argument, number of seconds expected");
                    return;
                }
                for (int i = 0; i < waitSec; ++i)
                {
                    Console.Write(String.Format("{0}, ", waitSec - i));
                    System.Threading.Thread.Sleep(1000);
                }
            }
            exit = true;
        }

        [MainCommand]
        private static void LuaTest(string p)
        {
            Lua lua = new Lua();
            
            LuaTestObj lto = new LuaTestObj();

            lua.RegisterFunction("echo", lto, lto.GetType().GetMethod("Echo"));
            lua.RegisterFunction("strings", lto, lto.GetType().GetMethod("GetStrings"));
            string c = String.Empty;
            while (c != "exit")
            {
                try
                {
                    lua.DoString(c);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                Console.Write("lua>");
                c = Console.ReadLine();
            }
        }

        [MainCommand]
        private static void LuaFile(string p)
        {
            Lua lua = new Lua();

            LuaTestObj lto = new LuaTestObj();

            lua.DoFile(p);
        }

        [MainCommand]
        private static void JintTest(string p)
        {
            JintEngine jint = new JintEngine();

            string c = String.Empty;
            while (c != "exit")
            {
                jint.SetFunction("cout", new Action<object>(Console.WriteLine));
                try
                {
                    jint.Run(c);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                Console.Write("jint>");
                c = Console.ReadLine();
            }
        }

        [MainCommand]
        private static void Send(string p)
        {
            lock (Telnet.Connection.List)
            {
                if (Telnet.Connection.List.Count > 0)
                {
                    Telnet.Connection c = (Telnet.Connection)Telnet.Connection.List[0];
                    c.Send(p, new UTF8Encoding());
                }
            }
        }

        [MainCommand]
        private static void Read(string p)
        {
            lock (Telnet.Connection.List)
            {
                if (Telnet.Connection.List.Count > 0)
                {
                    Telnet.Connection c = (Telnet.Connection)Telnet.Connection.List[0];
                    string l = c.GetNextMessage();
                    if (l == null)
                        Console.WriteLine("NULL (no new messages)");
                    else
                        Console.WriteLine(l);
                }
            }
        }

        [MainCommand]
        private static void RemoveDiacritics(string s)
        {
            s = s.Replace('ł', 'l').Replace('Ł', 'L').Normalize(NormalizationForm.FormD);
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < s.Length; i++)
            {
                if (CharUnicodeInfo.GetUnicodeCategory(s[i]) != UnicodeCategory.NonSpacingMark) sb.Append(s[i]);
            }

            Console.WriteLine(sb.ToString());
        }

        [MainCommand]
        private static void LuaPerf(string s)
        {
            LuaTestObj lto = new LuaTestObj();

            int howMany = int.Parse(s);
            List<long> times = new List<long>();
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            for (var i = 0; i < howMany; ++i)
            {
                sw.Restart();
                Script lp = new Script(@"Scripts/Test/LuaPerformanceTest.xml");
                lp.BindMethod(lto.GetType().GetMethod("Echo"), lto, "Cwrite");
                lp.BindMethod(lto.GetType().GetMethod("Read"), lto, "Cread");
                while (lp.ExecuteNext()) {
                    while (lto.Pause)
                    {
                        lock (Telnet.Connection.List)
                        {
                            if (Telnet.Connection.List.Count > 0)
                            {
                                Telnet.Connection c = (Telnet.Connection)Telnet.Connection.List[0];
                                string l = c.GetNextMessage();
                                if (l == null)
                                    Thread.Sleep(1000);
                                else
                                {
                                    lp.SetString(lto.VarName, l);
                                    lto.Pause = false;
                                }
                            }
                            else
                            {
                                Thread.Sleep(1000);
                            }
                            Console.Write('.');
                        }
                    }
                    Console.WriteLine("---Main Thread---");
                }
                sw.Stop();
                Console.WriteLine("Result=" + lp.GetReturn("Result"));
                times.Add(sw.ElapsedMilliseconds);
            }
            Console.WriteLine("------------");
            Console.WriteLine(String.Format("Mean time: {0}ms", times.Average()));
        }
        /*
        [MainCommand]
        private static void  JSPerf(string s)
        {
            LuaTestObj lto = new LuaTestObj();

            int howMany = int.Parse(s);
            List<long> times = new List<long>();
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            for (var i = 0; i < howMany; ++i)
            {
                sw.Restart();
                Script lp = new Script(@"Scripts/Test/JSPerformanceTest.xml");
                lp.BindMethod(lto.GetType().GetMethod("Echo"), lto, "Cwrite");
                lp.ExecuteNextStep();
                sw.Stop();
                Console.WriteLine("Result=" + lp.GetReturn("Result"));
                times.Add(sw.ElapsedMilliseconds);
            }
            Console.WriteLine("------------");
            Console.WriteLine(String.Format("Mean time: {0}ms", times.Average()));
        }
        */
        [MainCommand]
        private static void Cmd(string s)
        {
            bool exit = false;
            Test.ConsoleConnection cc = new Test.ConsoleConnection();
            
            while (!exit)
            {
                Console.Write("CMD>");
                string input = Console.ReadLine();
                input = input.Trim();
                if (input == String.Empty)
                    return;

                string command = String.Empty, parameters = String.Empty;
                if (input.Contains(' '))
                {
                    command = input.Substring(0, input.IndexOf(' '));
                    parameters = input.Substring(input.IndexOf(' ') + 1);
                }
                else
                {
                    command = input;
                }
                if (command.ToUpper() == "QUIT")
                {
                    return;
                }

                if (false)
                {

                }
                else
                {
                    Console.WriteLine("No such command");
                }

            }
        }
        #endregion

        [System.AttributeUsage(AttributeTargets.Method)]
        internal class MainCommand : Attribute
        { }
    }
    
}
